---
title: Regulated Currency and Deny List
description: You can create regulated currencies on Sui using the Coin Registry system. These coins include the ability to control access using a deny list.
keywords: [ regulated coin list, regulated coins, regulate coin, allow list, deny list, stablecoins, token example, create a token, how to create token, SUI token standards, coin registry ]
---

You can create regulated currencies on Sui, such as stablecoins. These coins are similar to other coins, like SUI, but include the ability to control access to the currency using a deny list.

The Coin Registry system provides enhanced regulatory features through the `sui::coin_registry` module:

1. **During initialization:** Use the `make_regulated()` function on the `CurrencyInitializer<T>` before calling `finalize()`.
2. **Regulatory tracking:** The registry automatically tracks regulatory status in the `RegulatedState` enum.
3. **Enhanced compliance:** Built-in support for global pause functionality and better ecosystem integration.

Advantages of these regulatory features include:

- **Centralized tracking:** Regulatory status is stored in the registry for easy discovery.
- **Global pause support:** Enhanced pause or unpause functionality for emergency situations.
- **Compliance tooling:** Better integration with wallets, exchanges, and compliance systems.
- **Migration support:** Seamless migration from legacy regulated coins.

## `DenyList`

The `DenyList` is a singleton, shared object that the bearer of a `DenyCapV2` can access to specify a list of addresses that are unable to use a Sui core type. The initial use case for `DenyList`, however, focuses on limiting access to coins of a specified type. This is useful when creating a regulated coin on Sui that requires the ability to block certain addresses from using it as inputs to transactions. Regulated coins on Sui satisfy any regulations that require the ability to prevent known bad actors from having access to those coins.

:::info

The `DenyList` object is a system object that has the address `0x403`. You cannot create it yourself.

:::

To learn about the features available, see the [Currency Standard](/standards/currency.mdx) documentation and the `coin_registry` module in the Sui framework.

## Regulated coin example

The regulated coin example is in the `examples/regulated-coin` directory of the Sui repo. The example provides both TypeScript- and Rust-based command line access to an on-chain package that demonstrates some of the features of regulated coins on Sui.

<ImportContent source="prerequisites.mdx" mode="snippet" />

This topic assumes you are accessing the code from your own fork of the Sui repo. 

You do not need a Sui wallet to use this project, but having one available might help you visualize results.

This example assumes you're familiar with publishing packages on Sui and the Move language. For more detailed guides on example dApps, see [App Examples](../app-examples.mdx). For more information on the Move language, see [The Move Book](https://move-book.com/).

## Publishing to a network

You publish the smart contract to a network the same way as any other package. See "Hello, World!"(/guides/developer/getting-started/hello-world.mdx) if you would like more details on the publishing process. 

The example includes a `publish.sh` file that you can run to automate the publishing. The script assumes you are publishing to Testnet, so be sure to update it before running if you plan to run on a local network or Devnet.

The publish script also creates the necessary `.env` files in each of the frontend folders. If you don't use the script, you must create the `.env` file manually and provide the values for the variables the frontend expects to find. Even if you use the script, you must provide the `ADMIN_SECRET_KEY` and its value.

:::warning

Take care not to expose the secret key for your address to the public.

:::

| Constant name | Description |
| --- | --- |
| `PACKAGE_ID` | Object ID of the package you publish. This data is part of the response that Sui provides on publish. |
| `ADMIN_SECRET_KEY` | The secret key for the address that publishes the package. You can use `sui keytool export --key-identity <SUI-ADDRESS>` or a wallet UI to get the value. Take care not to expose the value to the public. |
| `ADMIN_ADDRESS` | The address that publishes the contract. |
| `DENY_CAP_ID` | Deny capability object ID. This data is part of the response that Sui provides on publish. |
| `TREASURY_CAP_ID` | The treasury cap object ID that allows the bearer to mint new coins. This data is part of the response that Sui provides on publish. |
| `MODULE_NAME` | The name of the module you publish. |
| `COIN_NAME` | The name of your regulated coin.  |
| `SUI_FULLNODE_URL` | The URL to the full node network that processes transactions. For Testnet this value is `https://fullnode.testnet.sui.io:443`. |

## Smart contract

The example uses a single file to create the smart contract for the project (`regulated_coin.move`). The contract defines the regulated coin when you publish it to the network. The treasury capability (`TreasuryCap`) and deny capability (`DenyCapV2`) are transferred to the address that publishes the contract. The `TreasuryCap` permits the bearer to mint or burn coins (`REGULATED_COIN` in this example), and the `DenyCapV2` bearer can add and remove addresses from the list of unauthorized users. 

<details>

<summary>

`regulated_coin.move`

</summary>

<ImportContent source="examples/regulated-coin/move/sources/regulated_coin.move" mode="code" />

</details>

### Creating regulated coins

The Sui Currency Standard provides a `new_currency_with_otw` function to create currency using a One-Time Witness. To make the currency regulated, the module provides a `make_regulated` function that initializes the regulated currency and returns a `DenyCapV2`. The `DenyCapV2` bearer can add and remove addresses from a list that controls, or regulates, access to the coin. This ability is a requirement for assets like stablecoins.

The TypeScript and Rust clients handle the call to the `coin` package's `mint` function. The `coin` package also includes a `mint_and_transfer` function you could use to perform the same task, but the composability of minting the coin in 1 command and transferring with another is preferable. Using 2 explicit commands allows you to implement future logic between the minting of the coin and the transfer. The structure of programmable transaction blocks means you're still making and paying for a single transaction on the network.

<Tabs groupId="code-language">

<TabItem label="TypeScript" value="typescript">

<ImportContent source="examples/regulated-coin/ts-client/src/main.ts" mode="code" tag="mint" />

</TabItem>

<TabItem label="Rust" value="rust">

<ImportContent source="examples/regulated-coin/rust-client/src/tx_run/coin.rs" mode="code" tag="mint" />

</TabItem>

</Tabs>

For all `Coin` functions available, see the Sui framework [`coin` module documentation](https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/docs/sui/coin.md). The following functions are the most common. 

<details>

<summary>

  `coin::mint<T>`

</summary>

<ImportContent source="crates/sui-framework/packages/sui-framework/sources/coin.move" mode="code" fun="mint" />

</details>
<details>

<summary>

  `coin::mint_balance<T>`

</summary>

<ImportContent source="crates/sui-framework/packages/sui-framework/sources/coin.move" mode="code" fun="mint_balance" />

</details>

<details>

<summary>

  `coin::mint_and_transfer<T>`

</summary>

<ImportContent source="crates/sui-framework/packages/sui-framework/sources/coin.move" mode="code" fun="mint_and_transfer" />

</details>

<details>

<summary>

  `coin::burn<T>`

</summary>

<ImportContent source="crates/sui-framework/packages/sui-framework/sources/coin.move" mode="code" fun="burn" />

</details>

### Manage deny list

For the ability to manage the addresses assigned to the deny list for your coin, the frontend code provides a few additional functions. These additions call the `deny_list_v2_add` and `deny_list_v2_remove` functions in the `coin` module.

If you add an address to the deny list, you might notice that you can still send tokens to that address. If so, that's because the address is still able to receive coins until the end of the epoch in which you called the function. If you try to send the regulated coin from the now blocked address, your attempt results in an error. After the next epoch starts, the address can no longer receive the coins, either. If you remove the address, it can receive coins immediately but must wait until the epoch after removal before the address can include the coins as transaction inputs.

To use these functions, you pass the address you want to either add or remove. The frontend function then calls the relevant move module in the framework, adding the `DenyList` object (`0x403`) and your `DenyCapV2` object ID. You receive the `DenyCapV2` ID at the time of publishing the smart contract. In this example, you add that value to the `.env` file that the frontend function reads from.

<Tabs groupId="code-language">

<TabItem label="TypeScript" value="typescript">

<ImportContent source="examples/regulated-coin/ts-client/src/main.ts" mode="code" tag="deny" />

</TabItem>

<TabItem label="Rust" value="rust">

<ImportContent source="examples/regulated-coin/rust-client/src/tx_run/deny.rs" mode="code" tag="deny" />

</TabItem>

</Tabs>

## Related links

<RelatedLink to="/standards/closed-loop-token.mdx" />
<RelatedLink href="https://github.com/MystenLabs/sui/tree/main/examples/regulated-coin" label="Source code" desc="The source code in GitHub for this example." />
<RelatedLink to="/guides/developer/coin/in-game-token.mdx" />
<RelatedLink to="/guides/developer/coin/loyalty.mdx" />